package com.mopub.mobileads;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;

import csm.batteryinfo.R;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MraidView extends WebView {
	private static final String LOGTAG = "MraidView";

	private MraidBrowserController mBrowserController;
	private MraidDisplayController mDisplayController;

	private WebViewClient mWebViewClient;
	private WebChromeClient mWebChromeClient;

	private boolean mHasFiredReadyEvent;
	private final PlacementType mPlacementType;

	static class MraidListenerInfo {
		private OnExpandListener mOnExpandListener;
		private OnCloseListener mOnCloseListener;
		private OnReadyListener mOnReadyListener;
		private OnFailureListener mOnFailureListener;
		private OnCloseButtonStateChangeListener mOnCloseButtonListener;
		private OnOpenListener mOnOpenListener;
	}

	private MraidListenerInfo mListenerInfo;

	public enum ViewState {
		LOADING, DEFAULT, EXPANDED, HIDDEN
	}

	enum ExpansionStyle {
		ENABLED, DISABLED
	}

	enum NativeCloseButtonStyle {
		ALWAYS_VISIBLE, ALWAYS_HIDDEN, AD_CONTROLLED
	}

	enum PlacementType {
		INLINE, INTERSTITIAL
	}

	public MraidView(Context context) {
		this(context, ExpansionStyle.ENABLED,
				NativeCloseButtonStyle.AD_CONTROLLED, PlacementType.INLINE);
	}

	MraidView(Context context, ExpansionStyle expStyle,
			NativeCloseButtonStyle buttonStyle, PlacementType placementType) {
		super(context);
		mPlacementType = placementType;
		initialize(expStyle, buttonStyle);
	}

	private void initialize(ExpansionStyle expStyle,
			NativeCloseButtonStyle buttonStyle) {
		setScrollContainer(false);
		setBackgroundColor(Color.TRANSPARENT);

		setVerticalScrollBarEnabled(false);
		setHorizontalScrollBarEnabled(false);

		setOnTouchListener(new View.OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
				case MotionEvent.ACTION_UP:
					if (!v.hasFocus()) {
						v.requestFocus();
					}
					break;
				}
				return false;
			}
		});

		getSettings().setJavaScriptEnabled(true);

		mBrowserController = new MraidBrowserController(this);
		mDisplayController = new MraidDisplayController(this, expStyle,
				buttonStyle);

		mWebViewClient = new MraidWebViewClient();
		setWebViewClient(mWebViewClient);

		mWebChromeClient = new MraidWebChromeClient();
		setWebChromeClient(mWebChromeClient);

		mListenerInfo = new MraidListenerInfo();
	}

	public void destroy() {
		mDisplayController.destroy();
		super.destroy();
	}

	public void loadHtmlData(String data) {
		// If the string data lacks the HTML boilerplate, add it.
		if (data.indexOf("<html>") == -1) {
			data = "<html><head></head><body style='margin:0;padding:0;'>"
					+ data + "</body></html>";
		}

		// Inject the MRAID JavaScript bridge.
		String mraid = "file://"
				+ copyRawResourceToFilesDir(R.raw.mraid, "mraid.js");
		data = data.replace("<head>", "<head><script src='" + mraid
				+ "'></script>");

		loadDataWithBaseURL(null, data, "text/html", "UTF-8", null);
	}

	public void loadUrl(String url) {
		HttpClient httpClient = new DefaultHttpClient();
		StringBuffer out = new StringBuffer();

		try {
			HttpGet httpGet = new HttpGet(url);
			HttpResponse response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();

			if (entity != null) {
				InputStream is = entity.getContent();
				byte[] b = new byte[4096];
				for (int n; (n = is.read(b)) != -1;) {
					out.append(new String(b, 0, n));
				}
			}
		} catch (IllegalArgumentException e) {
			Log.d("MoPub", "Mraid loadUrl failed (IllegalArgumentException): "
					+ url);
			notifyOnFailureListener();
			return;
		} catch (ClientProtocolException e) {
			notifyOnFailureListener();
			return;
		} catch (IOException e) {
			notifyOnFailureListener();
			return;
		}

		loadHtmlData(out.toString());
	}

	private void notifyOnFailureListener() {
		if (mListenerInfo.mOnFailureListener != null) {
			mListenerInfo.mOnFailureListener.onFailure(this);
		}
	}

	// Controllers
	// /////////////////////////////////////////////////////////////////////////////////

	protected MraidBrowserController getBrowserController() {
		return mBrowserController;
	}

	protected MraidDisplayController getDisplayController() {
		return mDisplayController;
	}

	// Listeners
	// ///////////////////////////////////////////////////////////////////////////////////

	public void setOnExpandListener(OnExpandListener listener) {
		mListenerInfo.mOnExpandListener = listener;
	}

	public OnExpandListener getOnExpandListener() {
		return mListenerInfo.mOnExpandListener;
	}

	public void setOnCloseListener(OnCloseListener listener) {
		mListenerInfo.mOnCloseListener = listener;
	}

	public OnCloseListener getOnCloseListener() {
		return mListenerInfo.mOnCloseListener;
	}

	public void setOnReadyListener(OnReadyListener listener) {
		mListenerInfo.mOnReadyListener = listener;
	}

	public OnReadyListener getOnReadyListener() {
		return mListenerInfo.mOnReadyListener;
	}

	public void setOnFailureListener(OnFailureListener listener) {
		mListenerInfo.mOnFailureListener = listener;
	}

	public OnFailureListener getOnFailureListener() {
		return mListenerInfo.mOnFailureListener;
	}

	public void setOnCloseButtonStateChange(
			OnCloseButtonStateChangeListener listener) {
		mListenerInfo.mOnCloseButtonListener = listener;
	}

	public OnCloseButtonStateChangeListener getOnCloseButtonStateChangeListener() {
		return mListenerInfo.mOnCloseButtonListener;
	}

	public void setOnOpenListener(OnOpenListener listener) {
		mListenerInfo.mOnOpenListener = listener;
	}

	public OnOpenListener getOnOpenListener() {
		return mListenerInfo.mOnOpenListener;
	}

	// JavaScript injection
	// ////////////////////////////////////////////////////////////////////////

	protected void injectJavaScript(String js) {
		if (js != null)
			super.loadUrl("javascript:" + js);
	}

	protected void fireChangeEventForProperty(MraidProperty property) {
		String json = "{" + property.toString() + "}";
		injectJavaScript("window.mraidbridge.fireChangeEvent(" + json + ");");
		Log.d(LOGTAG, "Fire change: " + json);
	}

	protected void fireChangeEventForProperties(
			ArrayList<MraidProperty> properties) {
		String props = properties.toString();
		if (props.length() < 2)
			return;

		String json = "{" + props.substring(1, props.length() - 1) + "}";
		injectJavaScript("window.mraidbridge.fireChangeEvent(" + json + ");");
		Log.d(LOGTAG, "Fire changes: " + json);
	}

	protected void fireErrorEvent(String action, String message) {
		injectJavaScript("window.mraidbridge.fireErrorEvent('" + action
				+ "', '" + message + "');");
	}

	protected void fireReadyEvent() {
		injectJavaScript("window.mraidbridge.fireReadyEvent();");
	}

	protected void fireNativeCommandCompleteEvent(String command) {
		injectJavaScript("window.mraidbridge.nativeCallComplete('" + command
				+ "');");
	}

	private boolean tryCommand(URI uri) {
		String commandType = uri.getHost();
		List<NameValuePair> list = URLEncodedUtils.parse(uri, "UTF-8");
		Map<String, String> params = new HashMap<String, String>();
		for (NameValuePair pair : list) {
			params.put(pair.getName(), pair.getValue());
		}

		MraidCommand command = MraidCommandRegistry.createCommand(commandType,
				params, this);
		if (command == null) {
			fireNativeCommandCompleteEvent(commandType);
			return false;
		} else {
			command.execute();
			fireNativeCommandCompleteEvent(commandType);
			return true;
		}
	}

	/*
	 * Copies a file from res/raw to <destinationFilename> in the application
	 * file directory.
	 */
	private String copyRawResourceToFilesDir(int resourceId,
			String destinationFilename) {
		InputStream is = getContext().getResources()
				.openRawResource(resourceId);

		String destinationPath = getContext().getFilesDir().getAbsolutePath()
				+ File.separator + destinationFilename;
		File destinationFile = new File(destinationPath);
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(destinationFile);
		} catch (FileNotFoundException e) {
			return "";
		}

		byte[] b = new byte[8192];
		try {
			for (int n; (n = is.read(b)) != -1;) {
				fos.write(b, 0, n);
			}
		} catch (IOException e) {
			return "";
		} finally {
			try {
				is.close();
				fos.close();
			} catch (IOException e) {
			}
		}

		return destinationPath;
	}

	private class MraidWebViewClient extends WebViewClient {
		@Override
		public void onReceivedError(WebView view, int errorCode,
				String description, String failingUrl) {
			Log.d(LOGTAG, "Error: " + description);
			super.onReceivedError(view, errorCode, description, failingUrl);
		}

		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			Uri uri = Uri.parse(url);
			String scheme = uri.getScheme();

			if (scheme.equals("mopub"))
				return true;
			if (scheme.equals("mraid")) {
				tryCommand(URI.create(url)); // java.net.URI, not
												// android.net.Uri
				return true;
			}

			Intent i = new Intent();
			i.setAction(Intent.ACTION_VIEW);
			i.setData(Uri.parse(url));
			i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

			try {
				getContext().startActivity(i);
				return true;
			} catch (ActivityNotFoundException e) {
				return false;
			}
		}

		@Override
		public void onPageFinished(WebView view, String url) {
			if (!mHasFiredReadyEvent) {
				mDisplayController.initializeJavaScriptState();
				fireChangeEventForProperty(MraidPlacementTypeProperty
						.createWithType(mPlacementType));
				fireReadyEvent();
				if (getOnReadyListener() != null)
					getOnReadyListener().onReady(MraidView.this);
				mHasFiredReadyEvent = true;
			}
		}

		@Override
		public void onLoadResource(WebView view, String url) {
			Log.d(LOGTAG, "Loaded resource: " + url);
		}
	}

	private class MraidWebChromeClient extends WebChromeClient {
		@Override
		public boolean onJsAlert(WebView view, String url, String message,
				JsResult result) {
			Log.d(LOGTAG, message);
			return false;
		}
	}

	public interface OnExpandListener {
		public void onExpand(MraidView view);
	}

	public interface OnCloseListener {
		public void onClose(MraidView view, ViewState newViewState);
	}

	public interface OnReadyListener {
		public void onReady(MraidView view);
	}

	public interface OnFailureListener {
		public void onFailure(MraidView view);
	}

	public interface OnCloseButtonStateChangeListener {
		public void onCloseButtonStateChange(MraidView view, boolean enabled);
	}

	public interface OnOpenListener {
		public void onOpen(MraidView view);
	}
}
